package sm.hibernate.sql;

import java.util.Iterator;
import java.util.Map;

import org.apache.commons.collections.SequencedHashMap;
import org.hibernate.dialect.Dialect;
import org.hibernate.type.LiteralType;
import org.hibernate.util.StringHelper;

public class SMUpdate {
	private String tableName;
	private String[] primaryKeyColumnNames;
	private String[] indexColumnNames;
	private String versionColumnName;
	private String where;
	private String assignments;
	private String comment;

	private Map columns = new SequencedHashMap();
	private Map whereColumns = new SequencedHashMap();

	private Dialect dialect;

	public SMUpdate(Dialect dialect) {
		this.dialect = dialect;
	}

	public String getTableName() {
		return tableName;
	}

	public SMUpdate appendAssignmentFragment(String fragment) {
		if (assignments == null) {
			assignments = fragment;
		} else {
			assignments += ", " + fragment;
		}
		return this;
	}

	public SMUpdate setTableName(String tableName) {
		this.tableName = tableName;
		return this;
	}

	public SMUpdate setIndexColumnNames(String[] indexColumnNames) {
		this.indexColumnNames = indexColumnNames;
		return this;
	}

	public SMUpdate setPrimaryKeyColumnNames(String[] primaryKeyColumnNames) {
		this.primaryKeyColumnNames = primaryKeyColumnNames;
		return this;
	}

	public SMUpdate setVersionColumnName(String versionColumnName) {
		this.versionColumnName = versionColumnName;
		return this;
	}

	public SMUpdate setComment(String comment) {
		this.comment = comment;
		return this;
	}

	public SMUpdate addColumns(String[] columnNames) {
		for (int i = 0; i < columnNames.length; i++) {
			addColumn(columnNames[i]);
		}
		return this;
	}

	public SMUpdate addColumns(String[] columnNames, boolean[] updateable) {
		for (int i = 0; i < columnNames.length; i++) {
			if (updateable == null || updateable[i])
				addColumn(columnNames[i]);
		}
		return this;
	}

	public SMUpdate addColumns(String[] columnNames, String value) {
		for (int i = 0; i < columnNames.length; i++) {
			addColumn(columnNames[i], value);
		}
		return this;
	}

	public SMUpdate addColumn(String columnName) {
		return addColumn(columnName, "?");
	}

	public SMUpdate addColumn(String columnName, String value) {
		columns.put(columnName, value);
		return this;
	}

	public SMUpdate addColumn(String columnName, Object value, LiteralType type)
			throws Exception {
		return addColumn(columnName, type.objectToSQLString(value, dialect));
	}

	public SMUpdate addWhereColumns(String[] columnNames) {
		for (int i = 0; i < columnNames.length; i++) {
			addWhereColumn(columnNames[i]);
		}
		return this;
	}

	public SMUpdate addWhereColumns(String[] columnNames, String value) {
		for (int i = 0; i < columnNames.length; i++) {
			addWhereColumn(columnNames[i], value);
		}
		return this;
	}

	public SMUpdate addWhereColumn(String columnName) {
		return addWhereColumn(columnName, "=?");
	}

	public SMUpdate addWhereColumn(String columnName, String value) {
		whereColumns.put(columnName, value);
		return this;
	}

	public SMUpdate setWhere(String where) {
		this.where = where;
		return this;
	}

	public String toStatementString() {
		StringBuilder builder = new StringBuilder((columns.size() * 15)
				+ tableName.length() + 10);
		if (comment != null) {
			builder.append("/* ").append(comment).append(" */ ");
		}
		builder.append("update ").append(tableName).append(" set ");
		boolean assignmentsAppended = false;
		Iterator iter = columns.entrySet().iterator();
		while (iter.hasNext()) {
			Map.Entry e = (Map.Entry) iter.next();
			builder.append(e.getKey()).append('=').append(e.getValue());
			if (iter.hasNext()) {
				builder.append(", ");
			}
			assignmentsAppended = true;
		}
		if (assignments != null) {
			if (assignmentsAppended) {
				builder.append(", ");
			}
			builder.append(assignments);
		}

		boolean conditionsAppended = false;
		if (primaryKeyColumnNames != null || where != null
				|| !whereColumns.isEmpty() || versionColumnName != null) {
			builder.append(" where ");
		}
		if (primaryKeyColumnNames != null) {
			int primaryKeyCount = primaryKeyColumnNames.length;
			for (int i = 0; i < primaryKeyCount; i++) {
				String primaryKeyColumnName = primaryKeyColumnNames[i];
				builder.append("((").append(primaryKeyColumnName)
						.append("=?) or (? is null and ")
						.append(primaryKeyColumnName).append(" is null))");
				if (i < primaryKeyCount - 1) {
					builder.append(" and ");
				}
			}
			if (indexColumnNames != null && indexColumnNames.length > 0) {
				builder.append(" and ");
				builder.append(StringHelper.join("=? and ", indexColumnNames))
						.append("=?");
			}

			conditionsAppended = true;
		}
		if (where != null) {
			if (conditionsAppended) {
				builder.append(" and ");
			}
			builder.append(where);
			conditionsAppended = true;
		}
		iter = whereColumns.entrySet().iterator();
		while (iter.hasNext()) {
			final Map.Entry e = (Map.Entry) iter.next();
			if (conditionsAppended) {
				builder.append(" and ");
			}
			builder.append(e.getKey()).append(e.getValue());
			conditionsAppended = true;
		}
		if (versionColumnName != null) {
			if (conditionsAppended) {
				builder.append(" and ");
			}
			builder.append(versionColumnName).append("=?");
		}

		return builder.toString();
	}
}
